Django 汎用ビューを使ってみよう
Django 汎用ビューについて
これまではビューを手作業で作成してテンプレートファイルをレンダリングしてきました。
Djangoには、汎用ビュー(GenericView) という便利なクラスベースビューが提供されていて、これを使うと少ないコーディングでアプリケーションを作成することができるようになります。
CreateView:データベースにデータを追加するフォームを提供するビュー
UpdateView:データベースにあるデータを編集するフォームを提供するビュー
DeleteView:データベースにあるデータを削除するためのビュー
DetailView:データベースにあるデータの詳細を表示するためのビュー
ListView:データベースにあるデータをリスト表示するビュー
TemplateView:テンプレートを使って表示するビュー(HTTP GETメソッド)
FormView:POSTメソッドで送信されたフォームの処理を行うビュー
CreateView と UpdateView が他の汎用ビューと異なる点は、フォームを作成することにあります。
これらを説明するためのアプリケーション formdemo を作成しましょう。
code: bash
$ python manage.py startapp formdemo
$ mkdir formdemo/templates
settings.py の INSTALLED_APPSに formdemo を追加します。
code: python
INSTALLED_APPS = [
# ...
'formdemo',
]
プロジェクトの urls.py に formdemo を追加します。
code: python
urlpatterns = [
# ...
path('formdemo/', include('formdemo.urls')),
]
CreateView
次に、ユーザ情報のためのモデルクラスを定義します。
code: models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField(blank=True)
nickname = models.CharField(max_length=32, blank=True)
about_you = models.TextField(blank=True)
これからフォームを作成するビューは次のようにCreateView を継承したUserCreateViewクラスを定義します。
code: formdemo/view.py
from django.views.generic import CreateView
from .models import User
class UserCreateView(CreateView):
model = User
fields = ('name', 'email', 'nickname', 'about_you')
template_name = 'user_form.html'
template_name で明示的にテンプレートファイルを指定していますが、省略した場合は
formdemo/user_form.html がテンプレートファイルとして使用されます。
また、テンプレートファイルをレンダリングするための render() の記述がないこともわかりますね。
テンプレート
code: formdemo/templates/user_form.html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success">Save User</button>
</form>
{% endblock %}
これをformdemo/urls.py に登録します。
code: formdemo/urls.py
from django.urls import path, include
from . import views
app_name = 'formdemo'
urlpatterns = [
path('add/', views.UserCreateView.as_view(), name='user_create'),
]
フォームを表示するだけであれば、これでOKです。
このフォームを送信するとエラーになってしまいますが...
https://gyazo.com/c5edc5b0ee3d0f145682b8ddc868a443
ListView
追加したユーザを一覧表示するListViewについて説明します。
ここで、データベースも作成しておきます。
code: bash
$ python manage.py makemigrations
python manage.py migrate
formdemo/views.py に ListVIewを継承した UserListViewクラスを追加します。
code: python
from django.views.generic import CreateView, ListView
from django.urls import reverse_lazy
from .models import User
class UserListView(ListView):
model = User
fields = ('name', 'email')
template_name = 'user_list.html'
class UserCreateView(CreateView):
model = User
fields = ('name', 'email', 'nickname', 'about_you')
template_name = 'user_form.html'
success_url = reverse_lazy('formdemo:user_list')
success_url でPOSTしたあとにリダイレクトされるURLをreverse_lazy()で指定しています。ここで、reverse() を使ってしまうと、クラス定義の段階ではURLの逆引きが登録できないのでエラーになってしまいます。
code: formdemo/templates/user_list.html
{% extends "base.html" %}
{% block content %}
<a href="{% url 'formdemo:user_create' %}"> ユーザ追加</a>
<h2>ユーザ一覧</h2>
{% for user in user_list %}
<p>
ユーザ:{{ user.name }} / Email:{{ user.email }}
</p>
{% endfor %}
{% endblock %}
これらを formdemo/views.py に登録します。
code: formdemo/views.py
from django.urls import path, include
from . import views
app_name = 'formdemo'
urlpatterns = [
path('list/', views.UserListView.as_view(), name='user_list'),
path('add/', views.UserCreateView.as_view(), name='user_create'),
]
https://gyazo.com/09127fe5eff9fe085389598868a08491
UpdateView
登録されているユーザ情報を修正するUpdateViewについて説明します。
ビューにUpdateViewを継承した UserUpdateViewクラスを追加します。
code: views.py
from django.views.generic import CreateView, UpdateView
from .models import User
from .forms import UserForm
class UserListView(ListView):
model = User
fields = ('name', 'email')
template_name = 'user_list.html'
class UserCreateView(CreateView):
model = User
fields = ('name', 'email', 'nickname', 'about_you')
template_name = 'user_form.html'
success_url = reverse_lazy('formdemo:user_list')
class UserUpdateView(UpdateView):
model = User
form_class = UserForm
template_name = 'user_update.html'
success_url = reverse_lazy('formdemo:user_list')
テンプレートuser_list.htmlを修正してuser_update へのリンクを設定します。
code: formdemo/templates/usr_list.html
{% extends "base.html" %}
{% block content %}
<a href="{% url 'formdemo:user_create' %}"> ユーザ追加</a>
<h2>ユーザ一覧</h2>
{% for user in user_list %}
<p>
ユーザ:{{ user.name }} / Email:{{ user.email }}
<a href="{% url 'formdemo:user_update' user.pk %}">修正</a> </p>
{% endfor %}
{% endblock %}
user_updateが呼び出すテンプレートファイルuser_update.htmlを作成します。
code: formdemo/templates/user_update.html
{% extends 'base.html' %}
{% block content %}
{{ form.as_p }}
{% endblock %}
これらを formdemo/views.py に登録します。
code: formdemo/views.py
from django.urls import path, include
from . import views
app_name = 'formdemo'
urlpatterns = [
path('list/', views.UserListView.as_view(), name='user_list'),
path('add/', views.UserCreateView.as_view(), name='user_create'),
path('edit/<int:pk>/',
views.UserUpdateView.as_view(), name='user_update'),
]
pk はプライマリーキーのことで、URLに与えられた数値になります。
UserUpdateViewはこのプライマリキーを使ってデータにアクセスします。
https://gyazo.com/4febf874c422ae5d5ccfc50ccf81be86
https://gyazo.com/936e7445d73bbfe1d6d472799fc8dca4
DetailView
登録されているユーザ情報を修正するDetailViewについても、基本的にはUpdateViewと同じ設定になります。
ビューにDetailViewを継承した UserDetailViewクラスを追加します。
code: formdemo/views.py
from django.views.generic import (
CreateView, UpdateView, ListView, DetailView
)
from django.urls import reverse_lazy
from .models import User
from .forms import UserForm
class UserListView(ListView):
model = User
fields = ('name', 'email')
template_name = 'user_list.html'
class UserCreateView(CreateView):
model = User
fields = ('name', 'email', 'nickname', 'about_you')
template_name = 'user_form.html'
success_url = reverse_lazy('formdemo:user_list')
class UserUpdateView(UpdateView):
model = User
form_class = UserForm
template_name = 'user_update.html'
success_url = reverse_lazy('formdemo:user_list')
class UserDetailView(DetailView):
model = User
form_class = UserForm
template_name = 'user_detail.html'
success_url = reverse_lazy('formdemo:user_list')
UserListViewで表示される各エントリに詳細を見るためのリンクを配置します。
code: formdemo/templates/user_list.html
{% extends "base.html" %}
{% block content %}
<a href="{% url 'formdemo:user_create' %}"> ユーザ追加</a>
<h2>ユーザ一覧</h2>
{% for user in user_list %}
<p>
ユーザ:{{ user.name }} / Email:{{ user.email }}
<a href="{% url 'formdemo:user_detail' user.pk %}">詳細</a> </p>
{% endfor %}
{% endblock %}
ユーザ詳細のテンプレートでは編集ができるリンクを配置しましょう。
code: formdemo/templates/user_detail.html
{% extends "base.html" %}
{% block content %}
<h2>ユーザ詳細</h2>
<table border="1">
<tr>
<th>name</th>
<th>email</th>
<th>nickname</th>
<th>about_you</th>
</tr>
<tr>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.nickname }}</td>
<td>{{ user.about_you }}</td>
</tr>
<a href="{% url 'formdemo:user_update' user.pk %}">修正</a> {% endblock %}
code: formdemo/urls.py
from django.urls import path, include
from . import views
app_name = 'formdemo'
urlpatterns = [
path('list/', views.UserListView.as_view(), name='user_list'),
path('add/', views.UserCreateView.as_view(), name='user_create'),
path('edit/<int:pk>/',
views.UserUpdateView.as_view(), name='user_update'),
path('show/<int:pk>/',
views.UserDetailView.as_view(), name='user_detail'),
]
https://gyazo.com/7c4711dc67a2216f22e7879f58be046d
https://gyazo.com/9cfb9eac76f00c7603910e61b9e746a0
DeleteView
こんどは、DeleteView を使って指定したデータを削除できるようにしてみましょう。
ユーザをリストするテンプレートに削除のためのリンクを追加します。
code: formdemo/templates/user_list.html
{% extends "base.html" %}
{% block content %}
<a href="{% url 'formdemo:user_create' %}"> ユーザ追加</a>
<h2>ユーザ一覧</h2>
{% for user in user_list %}
<p>
ユーザ:{{ user.name }} / Email:{{ user.email }}
<a href="{% url 'formdemo:user_detail' user.pk %}">詳細</a> <a href="{% url 'formdemo:user_delete' user.pk %}">削除</a> </p>
{% endfor %}
{% endblock %}
削除を確認するためのテンプレートは次のようにしましょう。
code: formdemo/templates/user_delete.html
{% extends "base.html" %}
{% block content %}
<h2>ユーザ削除確認</h2>
<p>{{ user.name }} を削除していいですか?</p>
<form method="post">
{% csrf_token %}
<button>削除</button>
</form>
{% endblock %}
DeleteView を継承する UserDeleteView クラスを定義します。
code: formdemo/views.py
from django.views.generic import (
CreateView, UpdateView, ListView, DetailView, DeleteView
)
from django.urls import reverse_lazy
from .models import User
from .forms import UserForm
class UserListView(ListView):
model = User
fields = ('name', 'email')
template_name = 'user_list.html'
class UserCreateView(CreateView):
model = User
fields = ('name', 'email', 'nickname', 'about_you')
template_name = 'user_form.html'
success_url = reverse_lazy('formdemo:user_list')
class UserUpdateView(UpdateView):
model = User
form_class = UserForm
template_name = 'user_update.html'
success_url = reverse_lazy('formdemo:user_list')
class UserDetailView(DetailView):
model = User
form_class = UserForm
template_name = 'user_detail.html'
success_url = reverse_lazy('formdemo:user_list')
class UserDeleteView(DeleteView):
model = User
form_class = UserForm
template_name = 'user_confirm_delete.html'
success_url = reverse_lazy('formdemo:user_list')
DeleteViewクラスは、GETリクエストでは確認ページを表示し、
POSTリクエストで該当データの削除を実行します。
しかし、アプリケーションによっては実際にはデータベースからレコードが削除するのではなく、有効フラグを消して論理的に削除するようなこともあります。こうしたときは、DeleteViewのdelelte()メソッドをオーバーライドして対応することになります。
ここで、template_name を省略するとformdemo/user_confirm_delete.htmlが選ばれます。
code: bash
$ python manage.py dbshell
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
auth_group crispy_user
auth_group_permissions django_admin_log
auth_permission django_content_type
auth_user django_migrations
auth_user_groups django_session
auth_user_user_permissions formdemo_user
authdemo_user
sqlite> select * from formdemo_user;
4|freddie|freddie@example.com|freddie|
sqlite>
https://gyazo.com/2cfc29686f265317c92fb57422c81329
https://gyazo.com/871e3ff95ec1d57908338a8cd1921d92
https://gyazo.com/1f1b01634ecef558d47261af9164a58e
code: bash
sqlite> select * from formdemo_user;
sqlite>
汎用ビューを使うとデータベースのCRUD操作が簡単に行えることが確認できましたね。
参考: